
package com.gitee.jmash.rbac.service;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import com.gitee.jmash.common.excel.ExcelExport;
import com.gitee.jmash.common.tree.Tree;
import com.gitee.jmash.common.utils.UUIDUtil;
import com.gitee.jmash.core.orm.cdi.JpaTenantService;
import com.gitee.jmash.core.orm.jpa.TenantEntityManager;
import com.gitee.jmash.core.orm.tenant.TenantService;
import com.gitee.jmash.core.transaction.JakartaTransaction;
import com.gitee.jmash.core.utils.FilePathUtil;
import com.gitee.jmash.rbac.dao.ModuleDao;
import com.gitee.jmash.rbac.dao.OperationDao;
import com.gitee.jmash.rbac.dao.PermDao;
import com.gitee.jmash.rbac.dao.ResourceDao;
import com.gitee.jmash.rbac.dao.RolesPermsDao;
import com.gitee.jmash.rbac.entity.OperationEntity;
import com.gitee.jmash.rbac.entity.PermEntity;
import com.gitee.jmash.rbac.entity.ResourceEntity;
import com.gitee.jmash.rbac.excel.ResourceHeaderExport;
import com.gitee.jmash.rbac.model.ResourcePermResult;
import com.gitee.jmash.rbac.model.TreeResult;
import jakarta.enterprise.inject.Typed;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.transaction.Transactional.TxType;
import jakarta.validation.executable.ValidateOnExecution;
import jmash.protobuf.TableHead;
import jmash.rbac.protobuf.OperationReq;
import jmash.rbac.protobuf.PermReq;
import jmash.rbac.protobuf.ResourceExportReq;
import jmash.rbac.protobuf.ResourceReq;
import jmash.rbac.protobuf.ResourceType;

/**
 * 资源表 rbac_resource读服务.
 *
 * @author <a href="mailto:service@crenjoy.com">crenjoy</a>
 */
@Typed(ResourceRead.class)
@Transactional(TxType.SUPPORTS)
@JpaTenantService
@ValidateOnExecution
public class ResourceReadBean implements ResourceRead, JakartaTransaction {

  protected TenantEntityManager tem = new TenantEntityManager();

  protected ResourceDao resourceDao = new ResourceDao(this.tem);
  protected ModuleDao moduleDao = new ModuleDao(this.tem);
  protected PermDao permDao = new PermDao(this.tem);
  protected OperationDao operationDao = new OperationDao(this.tem);

  protected RolesPermsDao rolesPermsDao = new RolesPermsDao(this.tem);

  @PersistenceContext(unitName = "ReadRbac")
  public void setEntityManager(EntityManager entityManager) {
    this.tem.setEntityManager(entityManager, false);
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T extends TenantService> T setTenant(String tenant) {
    this.tem.setTenant(tenant);
    return (T) this;
  }

  @Override
  public EntityManager getEntityManager() {
    return this.tem.getEntityManager();
  }

  @Override
  public void setTenantOnly(String tenant) {
    this.tem.setTenantOnly(tenant);
  }

  @Override
  public String getTenant() {
    return this.tem.getTenant();
  }

  @Override
  public ResourceEntity findById(UUID entityId) {
    return resourceDao.find(entityId);
  }


  @Override
  public List<ResourceEntity> findListByReq(ResourceReq req) {
    return resourceDao.findListByReq(req);
  }

  @Override
  public List<TreeResult> findTreeList() {
    return resourceDao.findTreeListByReq(ResourceReq.newBuilder().build());
  }

  @Override
  public List<String> findResourcePerm(ResourceEntity entity) {
    List<String> result = null;
    String codePrefix = PermEntity.permCodePrefix(entity.getModuleCode(), entity.getResourceCode());
    // 资源权限
    List<PermEntity> perms =
        permDao.findListByReq(PermReq.newBuilder().setLikePermCode(codePrefix).build());
    // 资源对应操作编码
    for (PermEntity perm : perms) {
      if (result == null)
        result = new ArrayList<>();
      result.add(perm.getPermCode().substring(codePrefix.length()));
    }
    return result;
  }

  @Override
  public Tree<ResourceEntity, UUID> findTreeByReq(ResourceReq req) {
    Tree<ResourceEntity, UUID> tree = new Tree<ResourceEntity, UUID>();
    List<ResourceEntity> list = resourceDao.findListByReq(req);
    if (list.size() == 0) {
      return tree;
    }
    for (ResourceEntity entity : list) {
      tree.add(entity.getResourceId(), entity, entity.getParentId(), entity.getOrderBy());
    }
    return tree;
  }

  /** 校验资源路径. */
  @Override
  public boolean checkResourceUrl(String url) {
    ResourceEntity resourceEntity = resourceDao.findByUrl(url);
    return null != resourceEntity ? true : false;
  }

  @Override
  public String downloadResourceTemplate(ResourceExportReq request)
      throws FileNotFoundException, IOException {
    return exportResource(Collections.emptyList(), request.getTitle(), request.getTableHeadsList(),
        request.getFileName());
  }

  private String exportResource(List<ResourcePermResult> resourceList, String title,
      List<TableHead> tableHeads, String fileName) throws FileNotFoundException, IOException {
    ExcelExport excelExport = new ExcelExport();
    // 页签
    excelExport.createSheet(ResourceHeaderExport.TITLE);
    // 增加表头
    ResourceHeaderExport.addHeaderExport(excelExport, tableHeads);
    // 写标题
    title = StringUtils.isBlank(title) ? ResourceHeaderExport.TITLE : title;
    excelExport.writeTitle(title);
    // 写表头
    excelExport.writeHeader();
    // 写数据
    excelExport.writeListData(resourceList);
    // 增加数据校验
    excelExport.dictDataValidation(resourceList.size(), 3000);
    // 写入文件
    fileName = StringUtils.isBlank(fileName) ? title : fileName;
    String fileSrc = FilePathUtil.createNewTempFile(fileName + ".xlsx", "excel");
    String realFileSrc = FilePathUtil.realPath(fileSrc,true);
    excelExport.getWb().write(new FileOutputStream(realFileSrc));
    return realFileSrc;
  }

  @Override
  public String exportResource(ResourceExportReq request)
      throws FileNotFoundException, IOException {
    List<ResourcePermResult> list = findListByResource(request.getReq());
    return exportResource(list, request.getTitle(), request.getTableHeadsList(),
        request.getFileName());
  }


  @Override
  public List<ResourcePermResult> findListByResource(ResourceReq resourseReq) {
    Tree<ResourceEntity, UUID> tree = findTreeByReq(resourseReq);
    List<ResourceEntity> resourceEntities = StringUtils.isNotBlank(resourseReq.getResourceId())
        ? tree.getSelfChildsList(UUIDUtil.fromString(resourseReq.getResourceId()))
        : tree.getAllList();
    if (resourceEntities.isEmpty()) {
      return Collections.emptyList();
    }

    List<OperationEntity> operations =
        operationDao.findListByReq(OperationReq.getDefaultInstance());

    List<ResourcePermResult> results = new ArrayList<>();
    for (ResourceEntity resource : resourceEntities) {
      ResourcePermResult resourcePermResult = new ResourcePermResult();
      resourcePermResult.setResourceId(resource.getResourceId());
      resourcePermResult.setResourceCode(resource.getResourceCode());
      resourcePermResult.setResourceName(resource.getResourceName());
      resourcePermResult.setParentId(resource.getParentId());
      resourcePermResult.setIcon(resource.getIcon());
      resourcePermResult.setResourceType(resource.getResourceType());
      resourcePermResult.setTarget(resource.getTarget());
      resourcePermResult.setUrl(resource.getUrl());
      resourcePermResult.setHidden(resource.getHidden());
      resourcePermResult.setKeepAlive(resource.getKeepAlive());
      resourcePermResult.setModuleCode(resource.getModuleCode());
      
      // 目录无权限.
      if (ResourceType.catalog.equals(resource.getResourceType())) {
        results.add(resourcePermResult);
        continue;
      }

      String codePrefix =
          PermEntity.permCodePrefix(resource.getModuleCode(), resource.getResourceCode());
      // 资源权限
      Map<String, PermEntity> permsMap =
          permDao.findMapByReq(PermReq.newBuilder().setLikePermCode(codePrefix).build());
      // 排序
      List<PermEntity> newPerms = new ArrayList<>();
      for (OperationEntity operation : operations) {
        String permCode = codePrefix + operation.getOperationCode();
        if (permsMap.containsKey(permCode)) {
          newPerms.add(permsMap.get(permCode));
        }
      }
      resourcePermResult.setPermEntities(newPerms);
      results.add(resourcePermResult);
    }
    return results;
  }

  @Override
  public void close() throws Exception {
    CDI.current().destroy(this);
  }
}
